package fr.utbm.tx52;

import java.net.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.io.*;


/**
 * 
 * Thread qui garde la connexion avec un client
 * 
 * Protocole :
 * 
 * 		Réception de l'identifiant (numéro de téléphone d'un client)
 * 			# 04546421313
 * 		Ici le 04546421313 s'identifie
 * 
 * 		Réception d'une demande d'amité
 * 			+ 04546421313
 * 		Ici le client courant fait une demande d'amitié avec le 04546421313
 *
 *		Réception d'une suppression d'amité
 * 			- 04546421313
 * 		Ici le client courant fait une demande de suppression d'amitié avec le 04546421313
 * 
 * 		Réception du message associé à un fichier
 * 			% Voici le message associé au fichier
 * 
 * 		Réception d'un fichier son
 * 			s
 * 		Ensuite le client va écrire un tableau de byte dans la socket qui correspond au contenu du fichier son
 * 
 *  	Réception d'un fichier image
 * 			p
 * 		Ensuite le client va écrire un tableau de byte dans la socket qui correspond au contenu du fichier image
 * 
 * 		Commit du fichier
 * 			@ 04546421313 04546421313
 * 		L'utilisateur courant fait suivre les derniers fichiers envoyés aux téléphones passés en parametres
 * 	
 */
public class ClientThread implements Runnable {

	
	/**
	 * Type de fichier à receptionner
	 */
	private enum FileType {
		SOUND,
		IMAGE;
	};
	
	/**
	 * Taille du buffer pour l'envoi de fichiers
	 */
	//private static final int bufferSize = 4096;
	
	/**
	 * Thread du client
	 */
	private Thread _t;
	
	/**
	 * Socket entre le client et le serveur
	 */
	private Socket _socket;
	
	/**
	 * Flux de sortie
	 */
	private ObjectOutputStream _out;
	
	/**
	 * Flux d'entrée
	 */
	private ObjectInputStream _in;
	
	/**
	 * Un lien vers le serveur
	 */
	private Server _server;
	
	/**
	 * Le numéro de téléphone du client
	 */
	private String _clientID="";

	
	/**
	 * Les données de l'image en cours de transfert par l'utilisateur
	 */
	String imageFileName = null;
	String audioCommentFileName = null;
	String comment = null;
	

	private SimpleDateFormat timeStampFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
	
	
	/**
	 * Identification d'un client via son numéro de téléphone, normalement quand il se connecte
	 * 
	 * @param id
	 * 			L'identifiant du client (son numéro de téléphone)
	 */
	private void connexion(String id)
	{
		_clientID = id;
		_server.addClient(_clientID, this);
	}
	
	
	/**
	 * Demande d'ajout d'un ami
	 * 
	 * @param id
	 * 			Identifiant de l'ami demandé (son numéro de téléphone)
	 */
	private void addFriend(String id)
	{
		System.out.println("Demande d'amitié de " + _clientID + " pour " + id);

		// transmission demande amitié, bien sûr il faudra ajouter tous les tests qu'il faut
		// par exemple si l'on a déjà la liaison de faire, on préferera envoyer un message de confirmation
		_server.sendMessageToClient(id, "+", _clientID);
	}
	
	
	/**
	 * Demande de suppression d'amitié
	 * 
	 * @param id
	 * 			Identifiant de l'ami a supprimer (son numéro de téléphone)
	 */
	private void deleteFriend(String id)
	{		
		System.out.println("Demande de suppression d'amitié de " + _clientID + " pour " + id);
	}
	
	
	/**
	 * Demande d'un partage d'un média
	 * 
	 * @param ids
	 * 			Liste des id des clients avec qui on va partager l'image
	 */
	private void share(List<String> ids)
	{
		System.out.println("==========================================");
		
		System.out.println(_clientID + " a validé l'envoi d'une image à :");
		for (String id : ids) {
			System.out.println("	- " + id);
		}
		
		System.out.println("Commentaire écrit associé à l'image : " + comment);
		System.out.println("Nom du fichier de l'image : " + imageFileName);
		
		if (audioCommentFileName != null) {
			System.out.println("Nom du fichier audio associé à l'image : " + audioCommentFileName);
		} else {
			System.out.println("Aucun commentaire audio associé à l'image");
		}
		
		System.out.println("==========================================");
		
		// envoi des fichiers au client
		for (String id : ids) {
			_server.sendMedia(id, _clientID, imageFileName, audioCommentFileName, comment);
		}
		
		// Réinitialisation des valeurs
		imageFileName = null;
		audioCommentFileName = null;
		comment = null;
	}
	
	
	/**
	 * Ajout d'un commentaire écrit pour l'image en cours
	 * 
	 * @param comment
	 * 				Le contenu du commentaire
	 */
	private void setComment(String comment) {
		this.comment = comment;
		System.out.println("Ajout d'un commentaire pour l'image en cours : " + _clientID + " : " + comment);
	}
	
	
	/**
	 * Réception d'un fichier
	 * 
	 * Le fichier sera nommé numeroTelephone_date pour éviter les doublons
	 * 
	 * @param FileType
	 * 				Type de fichier que l'on receptionne
	 */
	private void receiveFile(FileType f) {
		
		System.out.println("Reception d'un fichier de " + _clientID);
		
		try {

			String fileName = _clientID + "_" + timeStampFormat.format(new Date());
			FileOutputStream fos = null;
			
			if ( f == FileType.SOUND ) {
				fos = new FileOutputStream(fileName + ".3gp");
				audioCommentFileName = fileName + ".3gp";
				System.out.println("Nom du fichier à créer : " + audioCommentFileName);
			} 
			else if ( f == FileType.IMAGE ){
				fos = new FileOutputStream(fileName + ".jpg");
				imageFileName = fileName + ".jpg";
				System.out.println("Nom du fichier à créer : " + imageFileName);
			}						
	
			System.out.println("Nom du fichier à créer : " + fileName);
	
			byte[] buffer = (byte[])_in.readObject();
			fos.write(buffer);
			
			fos.flush();
			fos.close();
			
		} catch(Exception e) {
			System.out.println("Erreur lors de la reception du fichier : " + e.getMessage());
		}
			
	}
	
	
	/**
	 * Connexion d'un client
	 * 
	 * @param s
	 * 			La socket utilisée
	 * @param server
	 * 			Un lien vers le serveur
	 */
	ClientThread(Socket s, Server server)
	{
		_server = server;
		_socket = s;
		try
		{
			_out = new ObjectOutputStream(_socket.getOutputStream());
			_in = new ObjectInputStream(_socket.getInputStream());
		}
		catch (IOException e){ }

		// on lance le thread du client
		_t = new Thread(this);
		_t.start();
	}

	
	
	/**
	 * Lancement du thread qui va reçevoir les messages
	 */
	public void run()
	{
		try
		{

			Object message;

			while ( (message = _in.readObject()) != null) {
				
				System.out.println("> message reçu : '" + message.toString() + "'");
				
				if ( message instanceof String) {
				
					String strMessage = (String)message;
					
					// le client déclare son identité
					if (strMessage.startsWith("#")) { 
						connexion(strMessage.split(" ")[1]);
					}
					
					// demande d'amitié
					else if (strMessage.startsWith("+")) {	
						addFriend(strMessage.split(" ")[1]);
					}
					
					// supprimer une amitié
					else if (strMessage.startsWith("-")) {	
						deleteFriend(strMessage.split(" ")[1]);
					}
					
					// demande de partage
					else if (strMessage.startsWith("@")) {	
						String[] tab = strMessage.split(" ");
						
						List<String> ids = new ArrayList<String>();
						for (int i = 1; i<tab.length; ++i) {
							ids.add(tab[i]);
						}
						
						share(ids);
					}
					
					// commentaire du fichier
					else if (strMessage.startsWith("%")) {	
						setComment(strMessage.substring(2));
					}
					
					// on va recevoir un fichier son
					else if (strMessage.startsWith("s")) {
						receiveFile(FileType.SOUND);
					}
					
					// on va recevoir une image
					else if (strMessage.startsWith("p")) {
						receiveFile(FileType.IMAGE);
					}					
					
				}
				else {
					System.out.println("Réception d'un message qui n'est pas string ?" + _clientID + ")");
				}
			}

		}
		catch (Exception e){ 
			System.out.println("Erreur lors de la lecture pour " + _clientID + " : " + e.getMessage());
		}
		finally // finally se produira le plus souvent lors de la deconnexion du client
		{
			try
			{
				_server.delClient(_clientID);
				_socket.close(); 
			}
			catch (IOException e){ }
		}
	}


	public void sendMessage(String type, String content)
	{
		synchronized (_out) {
			System.out.println("Envoi d'un message : " + type + " " + content);
			try {
				_out.writeObject(new String(type + " " + content));
				_out.flush();
			} catch (IOException e) {
				System.out.println("Erreur lors de l'envoi du message !");
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * Envoi d'un fichier sous la forme d'un tableau de bytes
	 * 
	 * @param path
	 * 			Le chemin vers le fichier
	 */
	public void sendFile(String path) {

		synchronized (_out) {
			
			File f = new File(path); 
			
			if( ! f.exists()) {
				System.out.println("Tentative d'envoi d'un fichier inexistant : " + path);
				return;
			}
			
			System.out.println("Envoi d'un fichier : " + path);
			
			try {
				FileInputStream fis = new FileInputStream(path);

				byte[] message = new byte[(int) f.length()];
				fis.read(message);
				_out.writeObject(message);
				_out.flush();
			}
			catch (Exception e) {
				System.out.println("Erreur lors de l'envoi du fichier : " + e.getMessage());
			}
		}
	}
	
}
